using System;
using System.IO;
using System.Net.Sockets;
using System.Text;

// The UserConnection class encapsulates the functionality of a TcpClient connection with streaming for a single user.
public class UserConnection : IDisposable
{


	public event LineReceivedEventHandler LineReceived;
	public delegate void LineReceivedEventHandler(UserConnection Sender, string Data);
	public event TerminatedEventHandler Terminated;
	public delegate void TerminatedEventHandler(UserConnection Sender);

	//====================================================================

	private byte[] ReadBuffer = new byte[modMain.BufferSize + 1];

	private TcpClient Client;
	private StreamWriter Writer;

	//====================================================================

	public UserConnection(TcpClient ExistingClient)
	{
		this.Client = ExistingClient;
		this.Writer = new StreamWriter(Client.GetStream());

		// This starts the asynchronous read thread. The data will be saved into readBuffer.
		this.Client.GetStream().BeginRead(ReadBuffer, 0, modMain.BufferSize, ReceiverCallback, null);
	}

	public UserConnection(TcpClient ExistingClient, LineReceivedEventHandler initialHandler)
	{
		if (initialHandler != null)
		{
			this.LineReceived += initialHandler;
		}
		
		this.Client = ExistingClient;
		this.Writer = new StreamWriter(Client.GetStream());

		// This starts the asynchronous read thread. The data will be saved into readBuffer.
		this.Client.GetStream().BeginRead(ReadBuffer, 0, modMain.BufferSize, ReceiverCallback, null);
	}

	//====================================================================

	// This is the callback function for TcpClient.GetStream.Begin. It begins an asynchronous read from a stream.
	private void ReceiverCallback(IAsyncResult ar)
	{
		int BytesRead;

		try {
			// Finish asynchronous read into readBuffer and get number of bytes read.
			BytesRead = Client.GetStream().EndRead(ar);

			if (BytesRead < 1)
			{
				Terminate();
				// If no bytes were read server has closed.
				return; // TODO: might not be correct. Was : Exit Sub
				// No need to continue listening.
			}

			//====================================================================
			// the old way doesn't support international characters:
			// strMessage = System.Text.Encoding.ASCII.GetString(ReadBuffer, 0, BytesRead) 
			//====================================================================

			// this does support international characters
			string strMessage = System.Text.Encoding.UTF8.GetString(ReadBuffer, 0, BytesRead);

			// Start a new asynchronous read into readBuffer.
			Client.GetStream().BeginRead(ReadBuffer, 0, modMain.BufferSize, ReceiverCallback, null);

			if (LineReceived != null) {
				LineReceived(this, strMessage);
			}
		}

		catch (Exception e) {
			Terminate();
		}
	}

	//====================================================================
	// Terminating connection and the underlying client
	//====================================================================

	// Initialize termination process when connection was lost
	private void Terminate()
	{
		if (Terminated != null) {
			Terminated(this);
		}
	}

	public void Dispose()
	{
		if ((Writer != null))
		{
			Writer.Close();
			Writer = null;
		}
		if ((Client != null))
		{
			// TODO: Think about shared access to this instance and synchronisation.
			Client.Close();
			Client = null;
		}
	}

	~UserConnection()
	{
		Dispose();
	}

	//====================================================================
	// The only public method to send data using this connection.
	//====================================================================

	public void SendData(string Data)
	{
		if (Writer == null || Data.Length < 1) return; 

		Writer.Write(Data);
		Writer.Flush();
		// Make sure all data is sent now.
	}

}
